package cc.ilzy.mall.coupon.service.impl;

import cc.ilzy.mall.coupon.interceptors.UserLoginInterceptor;
import cc.ilzy.mall.coupon.service.ICouponService;
import cc.ilzy.mall.enums.CouponCategoryEnum;
import cc.ilzy.mall.enums.CouponPulishEnum;
import cc.ilzy.mall.enums.CouponUserStateEnum;
import cc.ilzy.mall.exceptions.ExceptionEnum;
import cc.ilzy.mall.exceptions.MallException;
import cc.ilzy.mall.model.pojo.Coupon;
import cc.ilzy.mall.model.pojo.CouponRecord;
import cc.ilzy.mall.coupon.mappers.CouponRecordMapper;
import cc.ilzy.mall.coupon.service.ICouponRecordService;
import cc.ilzy.mall.model.pojo.User;
import cc.ilzy.mall.model.vo.CouponRecordVO;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.Version;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author ilzy
 * @since 2025-07-06
 */
@Service
public class CouponRecordServiceImpl extends ServiceImpl<CouponRecordMapper, CouponRecord>
        implements ICouponRecordService {
    @Autowired
    private ICouponService couponService;

    @Resource
    private RedissonClient redissonClient;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addCouponRecord(Long couponId, String category) {
        User user = UserLoginInterceptor.getCurrentUser();

        // 扣减库存 (使用的是数据库的乐观锁机制)
        // 使用Redis分布式锁可以避免高并发下的库存超卖问题
        RLock redissonLock = redissonClient.getLock("coupon:stock:lock:" + couponId);
        try {
            boolean lock = redissonLock.tryLock(1, TimeUnit.MINUTES);
            if (!lock) {
                throw new MallException(ExceptionEnum.COUPON_GET_FAIL);
            }

            Coupon coupon = couponService.lambdaQuery()
                                         .eq(Coupon::getId, couponId)
                                         .eq(Coupon::getCategory, category)
                                         .eq(Coupon::getPublish, CouponPulishEnum.PUBLISH.name())
                                         .gt(Coupon::getStock, 0)
                                         .le(Coupon::getStartTime, DateUtil.date())
                                         .ge(Coupon::getEndTime, DateUtil.date())
                                         .oneOpt()
                                         .orElseThrow(() -> new MallException(ExceptionEnum.COUPON_GET_FAIL));

            Long count = lambdaQuery().eq(CouponRecord::getCouponId, couponId)
                                      .eq(CouponRecord::getUserId, user.getId())
                                      .count();
            // 检查用户领取优惠券的次数是否超过限制
            if (count > coupon.getUserLimit()) {
                throw new MallException(ExceptionEnum.COUPON_OUT_OF_LIMIT);
            }
            boolean flag = couponService.lambdaUpdate().eq(Coupon::getId, couponId).gt(Coupon::getStock, 0)
                                        // .eq(Coupon::getVersion, coupon.getVersion())
                                        .setSql("stock = stock - 1")
                                        // .setIncrBy(Coupon::getVersion, 1)
                                        .update();
            // 扣减库存成功，添加领取记录
            if (!flag) {
                throw new MallException(ExceptionEnum.COUPON_GET_FAIL);
            }
            CouponRecord couponRecord = new CouponRecord();
            couponRecord.setCouponId(couponId)
                        .setUserId(user.getId())
                        .setUseState(CouponUserStateEnum.NEW.name())
                        .setUserName(user.getName())
                        .setCouponTitle(coupon.getCouponTitle())
                        .setPrice(coupon.getPrice())
                        .setConditionPrice(coupon.getConditionPrice())
                        .setStartTime(coupon.getStartTime())
                        .setEndTime(coupon.getEndTime());
            save(couponRecord);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            redissonLock.unlock();
        }

    }

    @Override
    public CouponRecordVO queryCouponRecordByCouponId(Long couponId) {
        User user = UserLoginInterceptor.getCurrentUser();
        CouponRecord couponRecord = lambdaQuery().eq(CouponRecord::getId, couponId)
                                                 .eq(CouponRecord::getUserId, user.getId())
                                                 .oneOpt()
                                                 .orElseThrow(() -> new MallException(ExceptionEnum.COUPON_NO_EXITS));
        return BeanUtil.toBean(couponRecord, CouponRecordVO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addNewCouponRecord(Long userId, String username) {
        try {
            UserLoginInterceptor.setCurrentUser(new User().setId(userId).setName(username));
            couponService.lambdaQuery()
                         .eq(Coupon::getCategory, CouponCategoryEnum.NEW_USER.name())
                         .list()
                         .forEach(coupon -> {
                             addCouponRecord(coupon.getId(), coupon.getCategory());
                         });
        } finally {
            UserLoginInterceptor.removeCurrentUser();
        }
    }
}
